/**
 * @fileOverview
 * @author Bart van Vliet - bart@dobots.nl
 */

/**
 * A polygon that can be edited by an end user
 *
 * @constructor
 * @param options - object with following keys:
 *   * pose (optional) - the first pose of the trace
 *   * lineSize (optional) - the width of the lines
 *   * lineColor (optional) - the createjs color of the lines
 *   * pointSize (optional) - the size of the points
 *   * pointColor (optional) - the createjs color of the points
 *   * fillColor (optional) - the createjs color to fill the polygon
 *   * lineCallBack (optional) - callback function for mouse interaction with a line
 *   * pointCallBack (optional) - callback function for mouse interaction with a point
 */
ROS2D.PolygonMarker = function(options) {
//  var that = this;
    createjs.Container.call(this);
    options = options || {};
    this.lineSize = options.lineSize || 3;
    this.lineColor = options.lineColor || createjs.Graphics.getRGB(0, 0, 255, 0.66);
    this.pointSize = options.pointSize || 10;
    this.pointColor = options.pointColor || createjs.Graphics.getRGB(255, 0, 0, 0.66);
    this.fillColor = options.pointColor || createjs.Graphics.getRGB(0, 255, 0, 0.33);
    this.lineCallBack = options.lineCallBack;
    this.pointCallBack = options.pointCallBack;

    // Array of point shapes
//  this.points = [];
    this.pointContainer = new createjs.Container();

    // Array of line shapes
//  this.lines = [];
    this.lineContainer = new createjs.Container();

    this.fillShape = new createjs.Shape();

    // Container with all the lines and points
    this.addChild(this.fillShape);
    this.addChild(this.lineContainer);
    this.addChild(this.pointContainer);
};

/**
 * Internal use only
 */
ROS2D.PolygonMarker.prototype.createLineShape = function(startPoint, endPoint) {
    var line = new createjs.Shape();
//  line.graphics.setStrokeStyle(this.strokeSize);
//  line.graphics.beginStroke(this.strokeColor);
//  line.graphics.moveTo(startPoint.x, startPoint.y);
//  line.graphics.lineTo(endPoint.x, endPoint.y);
    this.editLineShape(line, startPoint, endPoint);

    var that = this;
    line.addEventListener('mousedown', function(event) {
        if (that.lineCallBack !== null && typeof that.lineCallBack !== 'undefined') {
            that.lineCallBack('mousedown', event, that.lineContainer.getChildIndex(event.target));
        }
    });

    return line;
};

/**
 * Internal use only
 */
ROS2D.PolygonMarker.prototype.editLineShape = function(line, startPoint, endPoint) {
    line.graphics.clear();
    line.graphics.setStrokeStyle(this.lineSize);
    line.graphics.beginStroke(this.lineColor);
    line.graphics.moveTo(startPoint.x, startPoint.y);
    line.graphics.lineTo(endPoint.x, endPoint.y);
};

/**
 * Internal use only
 */
ROS2D.PolygonMarker.prototype.createPointShape = function(pos) {
    var point = new createjs.Shape();
    point.graphics.beginFill(this.pointColor);
    point.graphics.drawCircle(0, 0, this.pointSize);
    point.x = pos.x;
    point.y = -pos.y;

    var that = this;
    point.addEventListener('mousedown', function(event) {
        if (that.pointCallBack !== null && typeof that.pointCallBack !== 'undefined') {
            that.pointCallBack('mousedown', event, that.pointContainer.getChildIndex(event.target));
        }
    });

    return point;
};

/**
 * Adds a point to the polygon
 *
 * @param position of type ROSLIB.Vector3
 */
ROS2D.PolygonMarker.prototype.addPoint = function(pos) {
    var point = this.createPointShape(pos);
    this.pointContainer.addChild(point);
    var numPoints = this.pointContainer.numChildren;

    // 0 points -> 1 point, 0 lines
    // 1 point  -> 2 points, lines: add line between previous and new point, add line between new point and first point
    // 2 points -> 3 points, 3 lines: change last line, add line between new point and first point
    // 3 points -> 4 points, 4 lines: change last line, add line between new point and first point
    // etc

    if (numPoints < 2) {
        // Now 1 point
    }
    else if (numPoints < 3) {
        // Now 2 points: add line between previous and new point
        var line = this.createLineShape(this.pointContainer.getChildAt(numPoints-2), point);
        this.lineContainer.addChild(line);
    }
    if (numPoints > 2) {
        // Now 3 or more points: change last line
        this.editLineShape(this.lineContainer.getChildAt(numPoints-2), this.pointContainer.getChildAt(numPoints-2), point);
    }
    if (numPoints > 1) {
        // Now 2 or more points: add line between new point and first point
        var lineEnd = this.createLineShape(point, this.pointContainer.getChildAt(0));
        this.lineContainer.addChild(lineEnd);
    }

    this.drawFill();
};

/**
 * Removes a point from the polygon
 *
 * @param obj either an index (integer) or a point shape of the polygon
 */
ROS2D.PolygonMarker.prototype.remPoint = function(obj) {
    var index;
//  var point;
    if (obj instanceof createjs.Shape) {
        index = this.pointContainer.getChildIndex(obj);
//      point = obj;
    }
    else {
        index = obj;
//      point = this.pointContainer.getChildAt(index);
    }

    // 0 points -> 0 points, 0 lines
    // 1 point  -> 0 points, 0 lines
    // 2 points -> 1 point,  0 lines: remove all lines
    // 3 points -> 2 points, 2 lines: change line before point to remove, remove line after point to remove
    // 4 points -> 3 points, 3 lines: change line before point to remove, remove line after point to remove
    // etc

    var numPoints = this.pointContainer.numChildren;

    if (numPoints < 2) {

    }
    else if (numPoints < 3) {
        // 2 points: remove all lines
        this.lineContainer.removeAllChildren();
    }
    else {
        // 3 or more points: change line before point to remove, remove line after point to remove
        this.editLineShape(
            this.lineContainer.getChildAt((index-1+numPoints)%numPoints),
            this.pointContainer.getChildAt((index-1+numPoints)%numPoints),
            this.pointContainer.getChildAt((index+1)%numPoints)
        );
        this.lineContainer.removeChildAt(index);
    }
    this.pointContainer.removeChildAt(index);
//  this.points.splice(index, 1);

    this.drawFill();
};

/**
 * Moves a point of the polygon
 *
 * @param obj either an index (integer) or a point shape of the polygon
 * @param position of type ROSLIB.Vector3
 */
ROS2D.PolygonMarker.prototype.movePoint = function(obj, newPos) {
    var index;
    var point;
    if (obj instanceof createjs.Shape) {
        index = this.pointContainer.getChildIndex(obj);
        point = obj;
    }
    else {
        index = obj;
        point = this.pointContainer.getChildAt(index);
    }
    point.x = newPos.x;
    point.y = -newPos.y;

    var numPoints = this.pointContainer.numChildren;
    if (numPoints > 1) {
        // line before moved point
        var line1 = this.lineContainer.getChildAt((index-1+numPoints)%numPoints);
        this.editLineShape(line1, this.pointContainer.getChildAt((index-1+numPoints)%numPoints), point);

        // line after moved point
        var line2 = this.lineContainer.getChildAt(index);
        this.editLineShape(line2, point, this.pointContainer.getChildAt((index+1)%numPoints));
    }

    this.drawFill();
};

/**
 * Splits a line of the polygon: inserts a point at the center of the line
 *
 * @param obj either an index (integer) or a line shape of the polygon
 */
ROS2D.PolygonMarker.prototype.splitLine = function(obj) {
    var index;
    var line;
    if (obj instanceof createjs.Shape) {
        index = this.lineContainer.getChildIndex(obj);
        line = obj;
    }
    else {
        index = obj;
        line = this.lineContainer.getChildAt(index);
    }
    var numPoints = this.pointContainer.numChildren;
    var xs = this.pointContainer.getChildAt(index).x;
    var ys = this.pointContainer.getChildAt(index).y;
    var xe = this.pointContainer.getChildAt((index+1)%numPoints).x;
    var ye = this.pointContainer.getChildAt((index+1)%numPoints).y;
    var xh = (xs+xe)/2.0;
    var yh = (ys+ye)/2.0;
    var pos = new ROSLIB.Vector3({ x:xh, y:-yh });

    // Add a point in the center of the line to split
    var point = this.createPointShape(pos);
    this.pointContainer.addChildAt(point, index+1);
    ++numPoints;

    // Add a line between the new point and the end of the line to split
    var lineNew = this.createLineShape(point, this.pointContainer.getChildAt((index+2)%numPoints));
    this.lineContainer.addChildAt(lineNew, index+1);

    // Set the endpoint of the line to split to the new point
    this.editLineShape(line, this.pointContainer.getChildAt(index), point);

    this.drawFill();
};

/**
 * Internal use only
 */
ROS2D.PolygonMarker.prototype.drawFill = function() {
    var numPoints = this.pointContainer.numChildren;
    if (numPoints > 2) {
        var g = this.fillShape.graphics;
        g.clear();
        g.setStrokeStyle(0);
        g.beginStroke();
        g.beginFill(this.fillColor);
        g.moveTo(this.pointContainer.getChildAt(0).x, this.pointContainer.getChildAt(0).y);
        for (var i=1; i<numPoints; ++i) {
            g.lineTo(this.pointContainer.getChildAt(i).x, this.pointContainer.getChildAt(i).y);
        }
        g.closePath();
        g.endFill();
        g.endStroke();
    }
    else {
        this.fillShape.graphics.clear();
    }
};


ROS2D.PolygonMarker.prototype.__proto__ = createjs.Container.prototype;
